package com.csc.dao;


import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Repository;

import com.csc.domain.SystemAccount;
import com.csc.domain.SystemAccountType;
import com.csc.domain.SystemStatus;
import com.csc.domain.TransactionHistory;
import com.csc.service.PasswordController;

import constants.Constant;

@Repository
public class SystemAccountDAOImpl implements SystemAccountDAO {
	@PersistenceContext
	private EntityManager em;

	@Transactional
	public List<SystemAccount> getAccounts() {
		TypedQuery<SystemAccount> query = em.createQuery(
				"Select p From SystemAccount p", SystemAccount.class);
		return query.getResultList();
	}

	@Transactional
	public List<SystemAccount> getAccountsByUsername(String userName) {
		TypedQuery<SystemAccount> query = em.createQuery(
				"Select p From SystemAccount p WHERE p.accountLoginId = '"
						+ userName + "'", SystemAccount.class);
		return query.getResultList();
	}

	@Transactional
	public boolean createNewAccount(SystemAccount sysAcc, int accType,
			int status) {
		boolean check = true;
		SystemAccountType sysAccountType = em.getReference(
				SystemAccountType.class, accType);
		SystemStatus sysStatus = em.getReference(SystemStatus.class, status);

		sysAcc.setSystemAccountType(sysAccountType);
		sysAcc.setSystemStatus(sysStatus);

		em.persist(sysAcc);

		try {
			em.flush();
		} catch (PersistenceException exception) {
		}
		return check;
	}

	@Transactional
	public boolean updatePassword(String username, String password) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountPassword(password);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	@Transactional
	public boolean updateAdminName(String username, String fName, String lName,
			String mName) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountFirstName(fName);
			sysAccount.setAccountLastName(lName);
			sysAccount.setAccountMidName(mName);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	@Transactional
	public boolean updateAdminIdCardNumber(String username, String idCardNo) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountIDCardNumber(idCardNo);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	@Transactional
	public boolean updateAdminPhone(String username, String phone1,
			String phone2) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountPhone1(phone1);
			sysAccount.setAccountPhone2(phone2);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	@Transactional
	public boolean updateAdminEmail(String username, String email1,
			String email2) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountEmail1(email1);
			sysAccount.setAccountEmail2(email2);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	@Transactional
	public boolean updateAdminAddress(String username, String add1, String add2) {
		boolean check = true;
		SystemAccount sysAccount = em.find(SystemAccount.class, username);
		try {
			sysAccount.setAccountAddress1(add1);
			sysAccount.setAccountAddress2(add2);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	// Nguyen Vu Hung
	@Transactional
	public List<SystemAccount> getListAccountActive() {
		String queryString = "SELECT s FROM SystemAccount s Where s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable";
		TypedQuery<SystemAccount> query = em.createQuery(queryString,
				SystemAccount.class);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return query.getResultList();
	}

	// Nguyen Vu Hung
	public int countNumUserAdmin() {
		Query query = em
				.createQuery("SELECT COUNT(s.accountLoginId) FROM SystemAccount s WHERE s.systemAccountType.typeId = :role AND (s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable)");
		query.setParameter("role", Constant.ACCOUNT_TYPE_USER_ADMIN);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return ((Number) query.getSingleResult()).intValue();
	}

	// Nguyen Vu Hung
	public int countNumUserSupport() {
		Query query = em
				.createQuery("SELECT COUNT(s.accountLoginId) FROM SystemAccount s WHERE s.systemAccountType.typeId = :role AND (s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable)");
		query.setParameter("role", Constant.ACCOUNT_TYPE_USER_SUPPORT);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return ((Number) query.getSingleResult()).intValue();
	}

	// Nguyen Vu Hung
	public int countNumAccountAdmin() {
		Query query = em
				.createQuery("SELECT COUNT(s.accountLoginId) FROM SystemAccount s WHERE s.systemAccountType.typeId = :role AND (s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable)");
		query.setParameter("role", Constant.ACCOUNT_TYPE_ACCOUNT_ADMIN);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return ((Number) query.getSingleResult()).intValue();
	}

	// Nguyen Vu Hung
	public int countNumAccountSupport() {
		Query query = em
				.createQuery("SELECT COUNT(s.accountLoginId) FROM SystemAccount s WHERE s.systemAccountType.typeId = :role AND (s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable)");
		query.setParameter("role", Constant.ACCOUNT_TYPE_ACCOUNT_SUPPORT);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return ((Number) query.getSingleResult()).intValue();
	}

	// Nguyen Vu Hung
	public int countNumReportSupport() {
		Query query = em
				.createQuery("SELECT COUNT(s.accountLoginId) FROM SystemAccount s WHERE s.systemAccountType.typeId = :role AND (s.systemStatus.statusId = :active OR  s.systemStatus.statusId = :disable)");
		query.setParameter("role", Constant.ACCOUNT_TYPE_REPORT_SUPPORT);
		query.setParameter("active", Constant.STATUS_ID_ACTIVE);
		query.setParameter("disable", Constant.STATUS_ID_DISABLE);
		return ((Number) query.getSingleResult()).intValue();
	}

	public SystemAccount getAccountByLoginId(String loginid) {
		return em.find(SystemAccount.class, loginid);
	}

	// Truong Le Bao - Get List of Registered User (active & disable)
	@Override
	public List<SystemAccount> getRegisteredAccounts(String username) {
		// TODO Auto-generated method stub
		TypedQuery<SystemAccount> query;

		if (username.equals("admin")) {
			query = em
					.createQuery(
							"Select p From SystemAccount p Where p.systemStatus = :activeStatus OR p.systemStatus = :disableStatus",
							SystemAccount.class);
		} else {
			query = em
					.createQuery(
							"Select p From SystemAccount p Where (p.systemStatus = :activeStatus OR p.systemStatus = :disableStatus) AND p.systemAccountType.typeId >= 4 ",
							SystemAccount.class);
		}

		SystemStatus activeStatus = em.getReference(SystemStatus.class, 2);
		SystemStatus disableStatus = em.getReference(SystemStatus.class, 3);

		query.setParameter("activeStatus", activeStatus);
		query.setParameter("disableStatus", disableStatus);

		return query.getResultList();
	}

	// Truong Le Bao - Get Single Account by username
	@Override
	public SystemAccount getAccountByUsername(String userName) {
		// TODO Auto-generated method stub

		return em.find(SystemAccount.class, userName);
	}

	/**
	 * @author vuhuynh
	 */

	@Transactional
	public boolean doActiveAccountByAdmin(TransactionHistory trans,
			String accAff, int stt) {
		boolean check = true;
		SystemAccount sysAccAff = em.find(SystemAccount.class, accAff);
		SystemStatus sysStt = em.find(SystemStatus.class, stt);
		try {
			sysAccAff.setSystemStatus(sysStt);
			em.persist(trans);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	/**
	 * @author vuhuynh
	 */

	@Transactional
	public boolean doDisableAccountByAdmin(TransactionHistory trans,
			String accAff, int stt) {
		boolean check = true;
		SystemAccount sysAccAff = em.find(SystemAccount.class, accAff);
		SystemStatus sysStt = em.find(SystemStatus.class, stt);
		try {
			sysAccAff.setSystemStatus(sysStt);
			em.persist(trans);
		} catch (Exception e) {
			check = false;
		}
		return check;
	}

	/* @author:Tran Son Hoang---action: List new user in Admin page */
	@Transactional
	public List<TransactionHistory> getListAdminNewUsers() {
		// TODO Auto-generated method stub
		TypedQuery<TransactionHistory> query = em.createQuery("Select c from "
				+ TransactionHistory.class.getName()
				+ " c where c.actionTransaction= '"
				+ Constant.ACTION_CREATE_NEWUSER
				+ "' and c.accAffected.systemStatus ='1'",
				TransactionHistory.class);
		return query.getResultList();
	}

	/* @author:Tran Son Hoang---action: Search new users in admin/newUser.jsp */
	@Transactional
	public List<TransactionHistory> searchNewUserRegistrationByAdvanced(
			String name, String LoginId, String IdCardNumber, String Phone,
			String address, String Email, String role) {
		// TODO Auto-generated method stub
		TypedQuery<TransactionHistory> query = em.createQuery("select c from "
				+ TransactionHistory.class.getName()
				+ " c where c.accAffected.accountLoginId like '%" + LoginId
				+ "%' and c.accAffected.accountIDCardNumber like '%"
				+ IdCardNumber + "%' and c.accAffected.accountPhone1 like '%"
				+ Phone + "%' and c.accAffected.accountAddress1 like '%"
				+ address + "%' and c.accAffected.accountEmail1 like '%"
				+ Email
				+ "%' and c.accAffected.systemAccountType.typeName like '%"
				+ role + "%' and c.accAffected.accountFirstName like '%" + name
				+ "%' and c.accAffected.systemStatus ='1'",
				TransactionHistory.class);
		return query.getResultList();
	}

	/* @author:Tran Son Hoang---action: approve button in admin/newUser.jsp */
	@Transactional
	public boolean ApproveNewUserInAdminPage(
			TransactionHistory transactionHistory, String AccAffect, int status) {
		// TODO Auto-generated method stub
		boolean check = true;
		SystemAccount Accff = em.find(SystemAccount.class, AccAffect);
		SystemStatus systemStatus = em.find(SystemStatus.class, status);

		try {
			Accff.setSystemStatus(systemStatus);
			em.persist(Accff);
		} catch (Exception e) {
			// TODO: handle exception
			check = false;
		}
		return check;
	}

	/* @author:Tran Son Hoang---action: reject button in admin/newUser.jsp */
	@Transactional
	public boolean RejectNewUserInAdminPage(
			TransactionHistory transactionHistory, String AccAffect, int status) {
		// TODO Auto-generated method stub
		boolean check = true;
		SystemAccount Accff = em.find(SystemAccount.class, AccAffect);
		SystemStatus systemStatus = em.find(SystemStatus.class, status);

		try {
			Accff.setSystemStatus(systemStatus);
			em.persist(Accff);
		} catch (Exception e) {
			// TODO: handle exception
			check = false;
		}
		return check;
	}

	/* @author:Tran Son Hoang---action: create new account in admin/register.jsp */
	@Transactional
	public boolean createNewSystemAccount(String accountLoginId,
			String accountIDCardNumber, String accountFirstName,
			String accountLastName, String accountMidName,
			String accountPhone1, String accountPhone2, String accountEmail1,
			String accountEmail2, String accountAddress1,
			String accountAddress2, SystemAccountType role, SystemStatus stt)  {
		// TODO Auto-generated method stub
		BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
		boolean check = true;
		String Password = generateRandomString(8);
		String accountPassword=passwordEncoder.encode(Password);
		SystemAccount sys = new SystemAccount(accountLoginId, accountPassword,
				accountIDCardNumber, accountFirstName, accountLastName,
				accountMidName, accountPhone1, accountPhone2, accountEmail1,
				accountEmail2, accountAddress1, accountAddress2);
		sys.setSystemStatus(stt);
		sys.setSystemAccountType(role);
		try {
			em.persist(sys);
		} catch (Exception e) {
			// TODO: handle exception
			check = false;
		}

		return check;
	}

	/*
	 * @author:Tran Son Hoang---action: create transaction history in
	 * admin/register.jsp
	 */
	@Transactional
	public boolean createNewTransactionHistory(TransactionHistory trans) {
		// TODO Auto-generated method stub
		boolean check = false;
		try {
			em.persist(trans);
			check = true;
		} catch (Exception e) {
			// TODO: handle exception

		}
		return check;
	}

	@Override
	public String generateRandomString(int length) {
		// TODO Auto-generated method stub
		StringBuffer buffer = new StringBuffer();
		String characters = "";

		characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

		int charactersLength = characters.length();

		for (int i = 0; i < length; i++) {
			double index = Math.random() * charactersLength;
			buffer.append(characters.charAt((int) index));
		}
		return buffer.toString();
	}

	// Truong Le Bao
	@Override
	public List<SystemAccount> searchAdvanceRegisteredAccounts(String fullName,
			String username, String idcard, String phone, String address,
			String email, String role, String status, String loginID) {
		// TODO Auto-generated method stub

		// Get All Registered Users
		List<SystemAccount> listRegisteredUser;
		if(loginID.equals("admin")){
			listRegisteredUser = getRegisteredAccounts("admin");
		}else{
			listRegisteredUser = getRegisteredAccounts("");
		}
		
		// End - Get All Registered Users

		for (Iterator<SystemAccount> iterator = listRegisteredUser.iterator(); iterator
				.hasNext();) {
			SystemAccount acc = iterator.next();
			String name = acc.getAccountFirstName() + " "
					+ acc.getAccountMidName() + " " + acc.getAccountLastName();
			// Set Conditions to Filters
			boolean checkFullname = name.toLowerCase().contains(
					fullName.trim().toLowerCase());
			boolean checkusername = acc.getAccountLoginId().toLowerCase()
					.contains(username.trim().toLowerCase());
			boolean checkIDCard = acc.getAccountIDCardNumber().contains(
					idcard.trim().toLowerCase());
			boolean checkPhone = acc.getAccountPhone1().contains(phone.trim())
					|| acc.getAccountPhone2().contains((phone.trim()));
			boolean checkAddress = acc.getAccountAddress1().toLowerCase()
					.contains(address.toLowerCase().trim())
					|| acc.getAccountAddress2().toLowerCase()
							.contains(address.toLowerCase().trim());
			boolean checkEmail = acc.getAccountEmail1().toLowerCase()
					.contains(email.trim().toLowerCase())
					|| acc.getAccountEmail2().toLowerCase()
							.contains(email.trim().toLowerCase());
			boolean checkRole = acc.getSystemAccountType().getTypeName()
					.contains(role);
			boolean checkStatus = acc.getSystemStatus().getStatusName()
					.contains(status);
			// End - Set Conditions to Filters

			if (!checkFullname || !checkusername || !checkIDCard || !checkPhone
					|| !checkAddress || !checkEmail || !checkRole
					|| !checkStatus) {
				iterator.remove();
			}
		}

		return listRegisteredUser;
	}

	@Transactional
	public SystemAccount getNewUserDetail(String loginid) {
		// TODO Auto-generated method stub
		return em.find(SystemAccount.class, loginid);
	}
}